/*
 *  linux/mm/page.s
 *
 *  (C) 1991  Linus Torvalds
 */

#/* passed
# *  该文件包括页异常中断处理程序（中断14），主要分两种情况处理。
# * 一是由于缺页引起的页异常中断，通过调用do_no_page(error_code, address)来处理；
# * 二是由页写保护引起的页异常，此时调用页写保护处理函数do_wp_page(error_code, address)
# * 进行处理。其中的出错码(error_code)是由CPU 自动产生并压入堆栈的，出现异常时访问的
# * 线性地址是从控制寄存器CR2 中取得的。CR2 是专门用来存放页出错时的线性地址。
# */
# 处理缺页和页保护的异常

.globl page_fault

# 页异常处理函数
# 在trap_init进行了设置
page_fault:
	# 现场保护, 保存有发去的寄存器：dx, ax, cx?好像没有改动
	# 取错误码放到eax，硬件自动压入，同时将原eax值压栈
	xchgl %eax,(%esp)
	pushl %ecx
	pushl %edx
	push %ds
	push %es
	push %fs

	# 切换至内核数据段
	movl $0x10,%edx
	mov %dx,%ds
	mov %dx,%es
	mov %dx,%fs

	# 触发异常的地址
	movl %cr2,%edx
	pushl %edx				# 压入错误的地址
	pushl %eax				# 压入错误码

	# 检查最低位P，看看是否为0，不为0即为页级保护，执行do_wp_page；为0则做缺页处理
	testl $1,%eax			# error & 0x1
	jne 1f					# if != 0, 即最低位置1，goto 1f，即call do_wp_page，页保护
	call do_no_page			# else call 缺页函数处理
	jmp 2f					#
1:	call do_wp_page			# 页保护处理，用于写时复制
2:	addl $8,%esp			# 跳过压入的错码地址+错误码

	# 现场恢复后退出
	pop %fs
	pop %es
	pop %ds
	popl %edx
	popl %ecx
	popl %eax
	iret
#/*
# * 当处理器在转换线性地址到物理地址的过程中检测到以下两种条件时，
# * 就会发生页异常中断，中断14。
# *   o 当CPU 发现对应页目录项或页表项的存在位（Present）标志为0。
# *   o 当前进程没有访问指定页面的权限。
# * 对于页异常处理中断，CPU 提供了两项信息用来诊断页异常和从中恢复运行。
# * (1) 放在堆栈上的出错码。该出错码指出了异常是由于页不存在引起的还是违反了访问权限引起的；
# * 		在发生异常时CPU 的当前特权层；以及是读操作还是写操作。出错码的格式是一个32 位的长
# * 		字。但只用了最后的3 个比特位。分别说明导致异常发生时的原因：
# * 		位2(U/S) - 0 表示在超级用户模式下执行，1 表示在用户模式下执行；
# * 		位1(W/R) - 0 表示读操作，1 表示写操作；
# * 		位0(P) - 0 表示页不存在，1 表示页级保护。
# * (2) CR2(控制寄存器2)。CPU 将造成异常的用于访问的线性地址存放在CR2 中。异常处理程序可以
# * 		使用这个地址来定位相应的页目录和页表项。如果在页异常处理程序执行期间允许发生另一
# * 		个页异常，那么处理程序应该将CR2 压入堆栈中。
# */